﻿namespace LaoLuo
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    /// <summary>
    /// collection with fixed size, insertion order is preserved. element last inserted will be the first out when traversed
    /// </summary>
    /// <typeparam name="T">type of elements</typeparam>
    /// <typeparam name="TContainer">type of container</typeparam>
    public abstract class SizedCollection<T, TContainer> : ICollection<T>
        where TContainer : ICollection<T>, new()
    {
        /// <summary>
        /// time to wait for the write-lock
        /// </summary>
        private static TimeSpan wait = TimeSpan.FromSeconds(2);

        /// <summary>
        /// init a <see cref="SizedCollection&lt;T, TContainer>"/>
        /// </summary>
        /// <param name="size">fixed-size of this list</param>
        /// <param name="items">elements to fill</param>
        public SizedCollection(int size, params T[] items)
            : this(size, items.AsEnumerable())
        {
        }

        /// <summary>
        /// init a <see cref="SizedCollection&lt;T, TContainer>"/>
        /// </summary>
        /// <param name="size">fixed-size of this list</param>
        /// <param name="items">elements to fill</param>
        public SizedCollection(int size, IEnumerable<T> items = null)
        {
            Size = size;
            Container = new TContainer();
            if (items != null)
            {
                foreach (var item in items)
                {
                    this.Add(item);
                }
            }
        }

        /// <summary>
        /// gets fixed-size of this list
        /// </summary>
        public int Size { get; private set; }

        /// <summary>
        /// gets the element count of this list
        /// </summary>
        public int Count
        {
            get { return Container.Count; }
        }

        /// <summary>
        /// is read only or not
        /// </summary>
        bool ICollection<T>.IsReadOnly
        {
            get { return (Container as ICollection<T>).IsReadOnly; }
        }

        /// <summary>
        /// the container of elements
        /// </summary>
        protected TContainer Container { get; set; }

        /// <summary>
        /// whether the elements should be reversed or not when traversed
        /// </summary>
        protected abstract bool Reversed { get; }

        /// <summary>
        /// adds an item to the list
        /// </summary>
        /// <param name="item">item to add</param>
        public void Add(T item)
        {
            TimedLock.Call(
                delegate
                {
                    Append(item);
                    if (Container.Count > Size)
                    {
                        Truncate();
                    }
                },
            Container,
            wait);
        }

        /// <summary>
        /// clears the list
        /// </summary>
        public void Clear()
        {
            Container.Clear();
        }

        /// <summary>
        /// gets a value indicates whether <paramref name="item"/> is in this list
        /// </summary>
        /// <param name="item">item to check</param>
        /// <returns>if <paramref name="item"/> is in the list, return true, else false</returns>
        public bool Contains(T item)
        {
            return Container.Contains(item);
        }

        /// <summary>
        /// copies this list to an array
        /// </summary>
        /// <param name="array">array to copy to</param>
        /// <param name="arrayIndex">index to begin with</param>
        public void CopyTo(T[] array, int arrayIndex)
        {
            Container.CopyTo(array, arrayIndex);
        }

        /// <summary>
        /// removes the specified <paramref name="item"/> from list
        /// </summary>
        /// <param name="item">item to remove from this list</param>
        /// <returns>return true if <paramref name="item"/> is removed. return false if <paramref name="item"/> is not in this list</returns>
        public bool Remove(T item)
        {
            var ret = false;

            TimedLock.Call(
                delegate
                {
                    ret = Container.Remove(item);
                },
            Container,
            wait);

            return ret;
        }

        /// <summary>
        /// get enumerator
        /// </summary>
        /// <returns>enumerator</returns>
        public IEnumerator<T> GetEnumerator()
        {
            if (Reversed)
            {
                return Container.Reverse().GetEnumerator();
            }

            return Container.GetEnumerator();
        }

        /// <summary>
        /// get enumerator
        /// </summary>
        /// <returns>enumerator</returns>
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

        /// <summary>
        /// append a element to the collection
        /// </summary>
        /// <param name="item">element to append</param>
        protected virtual void Append(T item)
        {
            Container.Add(item);
        }

        /// <summary>
        /// remove the last-inserted element from the collection
        /// </summary>
        protected virtual void Truncate()
        {
            Container.Remove(Container.First());
        }
    }
}
